/*
*    Qizx/open 3.0 beta
*
* This code is the open-source version of Qizx.
* Copyright (C) 2004-2009 Axyana Software -- All rights reserved.
*
* The contents of this file are subject to the Mozilla Public License 
*  Version 1.1 (the "License"); you may not use this file except in 
*  compliance with the License. You may obtain a copy of the License at
*  http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
*  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
*  for the specific language governing rights and limitations under the
*  License.
*
* The Initial Developer of the Original Code is Xavier Franc - Axyana Software.
*
*/
using System;
using System.Xml;
using System.Xml.Schema;
using EvaluationException = System.Qizx.Api.EvaluationException;
using QName = System.Qizx.Api.QName;
using EvalContext = System.Qizx.XQuery.EvalContext;
using ExprDisplay = System.Qizx.XQuery.ExprDisplay;
using Focus = System.Qizx.XQuery.Focus;
using XQValue = System.Qizx.XQuery.XQValue;
using System.Qizx.Api;

namespace System.Qizx.XQuery.Op {

    /// <summary> class SchemaContext:</summary>
    public class SchemaContext : LeafExpression {
        virtual public bool SimpleName {
            get {
                return !startsFromType && steps.Count == 1;
            }

        }

        public bool startsFromType = false;

        public bool endsWithSlash = false;

        public System.Collections.ArrayList steps = new System.Collections.ArrayList();

        public SchemaContext(bool startsFromType) {
            this.startsFromType = startsFromType;
        }

        public virtual void addStep(QName step) {
            steps.Add(step);
        }
        public bool isSimpleName() {
            return !startsFromType && steps.Count == 1;
        }

        public virtual QName getStep(int rank) {
            return rank < 0 || rank >= steps.Count ? null : (QName)steps[rank];
        }

        internal bool SchemaKindTest { get; set; }

        /// <summary>
        /// Checks the base-type of a Node.
        ///  in absence of Schema accept only xs:untypedAtomic
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public bool acceptsNode(Node node) {
            // poor hack, just for XQUTS:
            try {
                if (steps.Count > 1)
                    return false;
                QName typeName = getStep(0);
                XdmNodeType nat = node.NodeType;

                if (typeName == null || typeName == XQType.untypedAtomic || typeName == XQType.anyType) {
                    return true; //CoShare add
                }

                IXdmSchemaInfo schemaInfo = node.SchemaInfo; //CoShare add

                #region schema-element(*) or schema-attribute(*) Test //HACK CoShare
                if (SchemaKindTest) {
                    if (nat == XdmNodeType.Element || nat == XdmNodeType.Document) {
                        #region CoShare added 2011-06-07
                        if (schemaInfo != null && schemaInfo.Validity == XmlSchemaValidity.Valid) {

                            #region check substitutionGroup
                            XmlQualifiedName head = node.NodeName;
                            bool isGroupOf = typeName.Equals(head);
                            if (!isGroupOf) {
                                XmlSchemaSet schemas = node.Schemas;
                                head = schemaInfo.SchemaElement.SubstitutionGroup;
                                while (head != null) {
                                    if (typeName.Equals(head)) {
                                        isGroupOf = true;
                                        break;
                                    }
                                    if (schemas != null) {
                                        XmlSchemaElement headDef = schemas.GlobalElements[head] as XmlSchemaElement;
                                        if (headDef != null) {
                                            head = headDef.SubstitutionGroup;
                                        } else {
                                            head = null;
                                        }
                                    } else {
                                        head = null;
                                    }
                                }
                            }
                            if (!isGroupOf) return false;
                            #endregion


                            return true;
                        }
                        #endregion
                    }

                    #region CoShare added 2011-06-07
                    //TODO schema-attirubte
                    if (nat == XdmNodeType.Attribute && schemaInfo != null && schemaInfo.Validity == XmlSchemaValidity.Valid) {
                        XmlSchemaType schemaType = schemaInfo.SchemaType;
                        if (schemaType == null && schemaInfo.SchemaAttribute != null) {
                            schemaType = schemaInfo.SchemaAttribute.AttributeSchemaType;
                            if (schemaType == null) {
                                schemaType = schemaInfo.SchemaAttribute.SchemaType;
                            }
                        }
                        while (schemaType != null) {
                            if (typeName.Equals(schemaType.QualifiedName)) {
                                return true;
                            }
                            schemaType = schemaType.BaseXmlSchemaType;
                        }
                        return false;
                    }
                    #endregion

                    return false;
                }
                #endregion

                if (nat == XdmNodeType.Element || nat == XdmNodeType.Document) {
                    #region CoShare added 2011-06-07
                    if (schemaInfo != null && schemaInfo.Validity == XmlSchemaValidity.Valid) {
                        XmlSchemaType schemaType = schemaInfo.SchemaType;
                        if (schemaType == null && schemaInfo.SchemaElement != null) {
                            schemaType = schemaInfo.SchemaElement.ElementSchemaType;
                            if (schemaType == null) {
                                schemaType = schemaInfo.SchemaElement.SchemaType;
                            }
                        }
                        while (schemaType != null) {
                            if (typeName.Equals(schemaType.QualifiedName)) {
                                return true;
                            }
                            schemaType = schemaType.BaseXmlSchemaType;
                        }
                        return false;
                    }
                    #endregion

                    return typeName == null ||
                           typeName == XQType.untypedAtomic || typeName == XQType.anyType;
                }

                #region CoShare added 2011-06-07
                if (nat == XdmNodeType.Attribute && schemaInfo != null && schemaInfo.Validity == XmlSchemaValidity.Valid) {
                    XmlSchemaType schemaType = schemaInfo.SchemaType;
                    if (schemaType == null && schemaInfo.SchemaAttribute != null) {
                        schemaType = schemaInfo.SchemaAttribute.AttributeSchemaType;
                        if (schemaType == null) {
                            schemaType = schemaInfo.SchemaAttribute.SchemaType;
                        }
                    }
                    while (schemaType != null) {
                        if (typeName.Equals(schemaType.QualifiedName)) {
                            return true;
                        }
                        schemaType = schemaType.BaseXmlSchemaType;
                    }
                    return false;
                }
                #endregion

                return typeName == null || typeName == XQType.untypedAtomic
                        || typeName == XQType.anyType;
                /* BugFix 4.1
                             if(nat == Node.ELEMENT || nat == Node.DOCUMENT)
                return typeName == null ||
                       typeName == XQType.untyped || typeName == XQType.anyType;
            return typeName == null || typeName == XQType.untypedAtomic
                    || typeName == XQType.anySimpleType;
                 */
            } catch (DataModelException e) {
                ///e.printStackTrace();
                return false;
            }
        }

        public override void Dump(ExprDisplay d) {
            //d.println("SchemaContext");
            d.property("startsFromType", "" + startsFromType);
            //d.display("steps", steps);
        }

        public override XQValue eval(Focus focus, EvalContext context) {
            throw new System.SystemException("SchemaContext not implemented");
        }
    }
}